package z.t.b.db.data;

import org.apache.commons.lang3.StringUtils;
import z.t.b.db.meta.Column;
import z.t.b.db.utils.StrUtils;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * 表数据
 */
public class TableData implements Cloneable{

    private String tableName;

    private List<String> primaryColumns = new ArrayList<>();
    private List<String> columnList = new ArrayList<>();
    // 跳过检查的列
    private List<String> skipCompareColumnList = new ArrayList<>();
    private Map<String, Column> columnMap = new HashMap<>();

    private List<RowData> dataList = new ArrayList<>();
    private Map<String, RowData> dataMap = new HashMap<>();

    public TableData copy(){
        TableData tableData = new TableData();
        tableData.setTableName(getTableName());
        tableData.setPrimaryColumns(getPrimaryColumns());
        tableData.setColumnList(getColumnList());
        tableData.setColumnMap(getColumnMap());
        return tableData;
    }

    public void addColumn(Column column, boolean isPrimary){
        String colName = column.getName();
        Column oldColumn = columnMap.get(colName);
        if(oldColumn != null){
            return;
        }
        columnList.add(colName);
        columnMap.put(colName, column);
        if(isPrimary){
            primaryColumns.add(colName);
        }
    }

    private void setColumnList(List<String> columnList) {
        this.columnList = columnList;
    }

    private void setColumnMap(Map<String, Column> columnMap) {
        this.columnMap = columnMap;
    }

    /**
     * 如果相同的主键数据已经存在，则不再添加
     * @param rowData
     */
    public void addRowData(RowData rowData){
        rowData.setOwner(this);
        String dataKey = getPrimaryDataKey(rowData);
        RowData oldRowData = dataMap.get(dataKey);
        if(oldRowData != null){
            return;
        }
        dataList.add(rowData);
        dataMap.put(dataKey, rowData);
    }

    /**
     *
     * @param rowData
     * @return
     */
    public String getPrimaryDataKey(RowData rowData){
        // 没有主键返回空
        if(primaryColumns.size() == 0){
            return null;
        }
        return primaryColumns.stream().map(col->rowData.getString(col, "")).collect(Collectors.joining("-"));
    }

    public RowData getRowDataByPrimaryKey(String primaryKey){
        return dataMap.get(primaryKey);
    }

    public RowData getRowDataByPrimaryKey(RowData srcRowData){
        String dataKey = getPrimaryDataKey(srcRowData);
        return getRowDataByPrimaryKey(dataKey);
    }

    public String getTableName() {
        return tableName;
    }

    public void setTableName(String tableName) {
        this.tableName = tableName;
    }

    public List<String> getPrimaryColumns() {
        return primaryColumns;
    }

    public void setPrimaryColumns(List<String> primaryColumns) {
        this.primaryColumns = primaryColumns;
    }

    public List<String> getColumnList() {
        return columnList;
    }

    /*public void setColumnList(List<String> columnList) {
        this.columnList = columnList;
    }*/

    public Map<String, Column> getColumnMap() {
        return columnMap;
    }

    /*public void setColumnMap(Map<String, Column> columnMap) {
        this.columnMap = columnMap;
    }*/

    public Column getColumnById(String colId){
        return this.columnMap.get(colId);
    }

    public List<RowData> getDataList() {
        return dataList;
    }

    public void setDataList(List<RowData> dataList) {
        this.dataList = dataList;
    }

    public Map<String, RowData> getDataMap() {
        return dataMap;
    }

    public void setDataMap(Map<String, RowData> dataMap) {
        this.dataMap = dataMap;
    }

    /**
     * 自身为目标数据，参数为元数据
     * @param srcData:
     * @return
     */
    public List<String> compareToSql(TableData srcData, boolean isDelete){
        List<String> sqlList = new ArrayList<>();
        for(RowData srcRowData : srcData.dataList){
            RowData tarRowData = this.getRowDataByPrimaryKey(srcRowData);
            // 不存在，insert
            if(tarRowData == null){
                sqlList.add(srcRowData.toInsertSql());
            }else {
                // 存在对比每一列，并生成update
                String updateSql = compareToUpdate(srcRowData, tarRowData);
                if (StringUtils.isNotBlank(updateSql)) {
                    sqlList.add(updateSql);
                }
            }
        }
        /**
         * 目标存在，源不存在的要delete？
         */
        if(isDelete){
            /**
             * todo
             */
        }
        return sqlList;
    }

    /**
     * 比较并生成update
     * @param srcRowData
     * @param tarRowData
     * @return
     */
    public String compareToUpdate(RowData srcRowData, RowData tarRowData){
        String setSql = getColumnList().stream().filter(col->{
            // 有些列会跳过检查
            if(skipCompareColumnList.contains(col)){
                return false;
            }
            Object srcVal = srcRowData.getValue(col, null);
            Object tarVal = tarRowData.getValue(col, null);
            String srcStr = "", tarStr = "";
            if(srcVal != null){
                srcStr = srcVal.toString();
            }
            if(tarVal != null){
                tarStr = tarVal.toString();
            }
            return !srcStr.equals(tarStr);
        }).map(col->{
            return col + "=" + srcRowData.getValueToSqlString(col, null);
        }).collect(Collectors.joining(","));
        if(StringUtils.isBlank(setSql)){
            return null;
        }
        StringBuilder sql = new StringBuilder();
        sql.append("UPDATE ").append(getTableName());
        sql.append(" SET ").append(setSql);
        sql.append(" WHERE ").append(primaryColumns.stream().map(col->{
            String val = srcRowData.getString(col, null);
            if(val == null){
                return "("+col + " IS NULL OR "+ col + "='')";
            }
            return col+"="+ StrUtils.getDbString(val);
        }).collect(Collectors.joining(" AND ")));
        return sql.toString();
    }

    public List<String> getSkipCompareColumnList() {
        return skipCompareColumnList;
    }

    public void setSkipCompareColumnList(List<String> skipCompareColumnList) {
        this.skipCompareColumnList = skipCompareColumnList;
    }
}
